package com.biz.primus.ms.oms.service.returns;

import com.biz.primus.base.exception.BizSilentException;
import com.biz.primus.model.oms.enums.CancelType;
import com.biz.primus.model.oms.enums.returns.AuditState;
import com.biz.primus.model.oms.enums.returns.ReturnState;
import com.biz.primus.model.oms.stream.ReturnStateChannel.ReturnStateStreamData;
import com.biz.primus.model.oms.vo.ExpressInfoVo;
import com.biz.primus.model.oms.vo.OmsItemVo;
import com.biz.primus.model.oms.vo.OmsOrderReturnVo;
import com.biz.primus.model.oms.vo.returns.*;
import com.biz.primus.ms.base.service.AbstractBaseService;
import com.biz.primus.ms.base.service.SequenceGenerator;
import com.biz.primus.ms.oms.dao.po.OmsOrder;
import com.biz.primus.ms.oms.dao.po.returns.OmsRefund;
import com.biz.primus.ms.oms.dao.po.returns.OmsReturn;
import com.biz.primus.ms.oms.dao.po.returns.OmsReturnItem;
import com.biz.primus.ms.oms.dao.po.returns.RefundState;
import com.biz.primus.ms.oms.dao.repository.OmsOrderRepository;
import com.biz.primus.ms.oms.dao.repository.OmsOrderReturnRepository;
import com.biz.primus.ms.oms.dao.repository.OmsRefundRepository;
import com.biz.primus.model.oms.exception.OmsExceptions;
import com.biz.primus.ms.oms.stream.OmsStateProcessor;
import com.biz.primus.ms.oms.stream.ReturnCollectionSource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.messaging.Message;
import org.springframework.messaging.support.MessageBuilder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.List;
import java.util.Objects;
import java.util.function.Predicate;
import java.util.stream.Collectors;

/**
 * @author  LGJ
 * @date  2017/11/16
 */
@Service
@Slf4j
@Transactional(rollbackFor = Exception.class)
public class OrderReturnStateServiceImpl extends AbstractBaseService implements OrderReturnStateService{


    @Autowired
    private OmsOrderReturnRepository returnRepository;
    
    @Autowired
    private OmsStateProcessor stateProcessor;

    @Autowired
    private OmsRefundRepository omsRefundRepository;

    @Autowired
    private ReturnCollectionSource returnCollectionSource;

    @Autowired
    private OmsOrderRepository omsOrderRepository;

    @Autowired
    private SequenceGenerator sequenceGenerator;

    @Override
    public void auditReturns(AuditRequestVo req) {
        OmsReturn omsReturn = findReturns(req);
        if(req.getAuditResult()==AuditState.pass){
            omsReturn.setReturnState(ReturnState.audited);
        }else{
            omsReturn.setReturnState(ReturnState.finished);
        }
        returnRepository.save(omsReturn);
    }

    @Override
    public void modifyReturns(ModifyReturnRequestVo req) {
        OmsReturn omsReturn = findReturns(req);

        if(omsReturn.getReturnState()==ReturnState.finished){
            throw  new BizSilentException(OmsExceptions.ORDER_NOT_FOUNT);
        }

        if(req.getNewRefundAmount()!=null){
            omsReturn.setReturnAmount(req.getNewRefundAmount());
        }

        if(req.getNewRefundAmount()==null&&req.getItems()!=null){
            Integer returnAmount = 0;
            for(ModifyReturnItemRequestVo vo : req.getItems()){
                 returnAmount+=vo.getNewFinalPrice()*vo.getNewQuantity();
            }
            omsReturn.setReturnAmount(returnAmount);
        }

        if(req.getItems()!=null){
           List<OmsReturnItem> omsReturnItem = omsReturn.getItems();
            omsReturnItem.stream().forEach(omsItem ->{
                req.getItems().stream().forEach(reqItem->{
                    if(Objects.equals(omsItem.getItemId(),reqItem.getReturnItemId())){
                        omsItem.setQuantity(reqItem.getNewQuantity());
                        omsItem.setFinalPrice(reqItem.getNewFinalPrice());
                    }
                });
            });
        }

        if(req.getNewRefundWays()!=null){
            OmsRefund omsRefund = omsRefundRepository.findByReturnId(omsReturn.getId());
            if(!Objects.isNull(omsRefund)){
                omsRefund.setRefundWays(req.getNewRefundWays());
                omsRefundRepository.save(omsRefund);
            }
        }
        returnRepository.save(omsReturn);
    }

    @Override
    public void modifyRefunds(RefundRequestVo refundRequestVo) {
        OmsRefund omsRefund = omsRefundRepository.findByReturnId(refundRequestVo.getReturnId());
        if(omsRefund.getRefundState()!= RefundState.success){
            throw  new BizSilentException(OmsExceptions.STATE_ERROR);
        }
        omsRefund.setRefundWays(refundRequestVo.getRefundWays());
        omsRefund.setRefundBank(refundRequestVo.getRefundBank());
        omsRefund.setRefundBankAccount(refundRequestVo.getRefundBankAccount());
        omsRefund.setRefundReceiver(refundRequestVo.getRefundReceiver());
        omsRefundRepository.save(omsRefund);
    }

    @Override
    public void fillReturnExpressInfo(UserFillReturnExpressInfoRequestVo req) {
        OmsReturn omsReturn = findReturns(req);
        ExpressInfoVo expressInfoVo = req.getExpressInfo();
        omsReturn.setExpressNumber(expressInfoVo.getExpressNumber());
        omsReturn.setExpectedExpressProviderText(expressInfoVo.getExpressProvider());
        omsReturn.setReturnState(ReturnState.returning);
        returnRepository.save(omsReturn);
    }

    @Override
    public void returnInbound(OmsReturnOperationRequest req) {
        //根据退单编码查询出退单

        Predicate<OmsReturn> p = (r-> r.getReturnState() == ReturnState.returning);
        p.or(r->r.getReturnState()==ReturnState.audited&&r.getAuditState()== AuditState.pass);

        OmsReturn omsReturn = returnStateChange(req, ReturnState.finished, p);

        returnRepository.save(omsReturn);

        delayer.executeAfterTransactionCommit(()->{
            ReturnStateStreamData payload = ReturnStateStreamData.builder().returnCode(omsReturn.getReturnCode())
                    .returnId(omsReturn.getId()).needInbound(true).needRefund(omsReturn.isNeedRefund()).build();
            Message<ReturnStateStreamData> msg = MessageBuilder.withPayload(payload).build();
            stateProcessor.returnFinishedOutput().send(msg);

        });
    }

    @Override
    public void cancelReturns(UserOperateReturnRequestVo req) {
        OmsReturn returns = findReturns(req);
        if(Objects.equals(returns.getUserId(),req.getUserId())){
            log.warn("退货单[{}]不是当前用户[{}]所属",returns.getReturnCode(),req.getUserId());
            throw new BizSilentException(OmsExceptions.RETURN_NOT_FOUNT,"退货单不是当前用户所属");
        }
        if (returns.getReturnState()!= ReturnState.begin) {
            throw new BizSilentException(OmsExceptions.STATE_ERROR);
        }
        returns.setCancelType(CancelType.canceled);
        returnRepository.save(returns);
    }

    @Override
    public void closeReturns(OmsReturnOperationRequest req) {
        OmsReturn returns = findReturns(req);
        if (returns.getReturnState() == ReturnState.finished) {
            throw new BizSilentException(OmsExceptions.STATE_ERROR);
        }
        returns.setCancelType(CancelType.closed);
        returnRepository.save(returns);
    }

    @Override
    public void applyforReturns(ApplyforReturnVo applyforReturnVo) {
        OmsOrder omsOrder = omsOrderRepository.findByOrderCode(applyforReturnVo.getOrderCode());

        if(Objects.isNull(omsOrder)){
            throw  new BizSilentException(OmsExceptions.ORDER_NOT_FOUNT);
        }

        OmsOrderReturnVo rvo = new OmsOrderReturnVo();
        rvo.setReturnState(ReturnState.begin);
        rvo.setOriginOrderCode(omsOrder.getOrderCode());
        rvo.setChannel(omsOrder.getChannel());
        rvo.setOrderSource(omsOrder.getOrderSource());
        rvo.setReturnSource("system");
        rvo.setCode(sequenceGenerator.generateNextCode(SequenceGenerator.CodeType.returns));
        rvo.setDepot(omsOrder.getDepotCode());
        rvo.setNeedRefund(omsOrder.getPaymentState().isFinished());
        rvo.setUserId(omsOrder.getUserId());
        rvo.setLiquidated(0);

        List<OmsItemVo> omsItemVos = applyforReturnVo.getApplyforReturnItemVos().stream().map(req ->{
            OmsItemVo omsItemVo =new OmsItemVo();
            omsItemVo.setCode(req.getProductCode());
            omsItemVo.setQuantity(req.getQuantity());
            return omsItemVo;
        }).collect(Collectors.toList());
        rvo.setItems(omsItemVos);
        rvo.setSelfRemark(applyforReturnVo.getRemark());
        Message<OmsOrderReturnVo> msg = MessageBuilder.withPayload(rvo).build();
        returnCollectionSource.returnDataOutput().send(msg);
    }

    private OmsReturn returnStateChange(OmsReturnResource source, ReturnState state, Predicate<OmsReturn> predicate){
        OmsReturn r = findReturns(source);
        if (!predicate.test(r)) {
            throw new BizSilentException(OmsExceptions.STATE_ERROR);
        }
        r.setReturnState(state);
        return r;
    }

    private OmsReturn findReturns(OmsReturnResource source) {
        OmsReturn returns = returnRepository.findByReturnCode(source.getReturnCode());
        if(returns==null){
            throw new BizSilentException(OmsExceptions.RETURN_NOT_FOUNT);
        }
        return returns;
    }


}
